package main

import (
	"context"
	"crypto/tls"
	"encoding/json"
	"fmt"
	"gopkg.in/yaml.v3"
	"io"
	"log"
	"net"
	"net/http"
	"os"
	"strings"
	"sync"
	"time"
)

const MAXFAILED = 3

type Config struct {
	ProxyUrl string     `yaml:"proxyUrl"`
	Port     int        `yaml:"port"`
	Domains  []Domain   `yaml:"domains"`
	Proxies  []*Fetcher `yaml:"proxies"`
}
type Domain struct {
	Domain     string         `yaml:"domain"`
	RandomUrls []string       `yaml:"randomUrls"`
	ReferUrls  []ReferUrlItem `yaml:"referUrls"`
}
type ReferUrlItem struct {
	Domain  string `yaml:"domain" json:"domain"`
	Url     string `yaml:"url" json:"url"`
	Percent int    `yaml:"percent" json:"percent"`
}
type ProxyItem struct {
	IP         string    `json:"ip"`
	Port       string    `json:"port"`
	User       string    `json:"user"`
	Password   string    `json:"password"`
	ExpireTime time.Time `json:"expireTime"`
}
type Resp struct {
	Code string      `json:"code"`
	Msg  string      `json:"msg"`
	Obj  []ProxyItem `json:"obj"`
}
type EcRespItem struct {
	UniqueKey  string `json:"uniqueKey"`
	ExpireTime string `json:"expireTime"`
}
type ECResp struct {
	Code int          `json:"code"`
	Msg  string       `json:"msg"`
	Data []EcRespItem `json:"data"`
}

func getConfig() Config {
	// 打开配置文件
	file, err := os.Open("./config.yaml")
	if err != nil {
		log.Fatalf("error opening file: %v", err)
	}
	defer file.Close()

	// 解析 YAML 内容
	var config Config
	decoder := yaml.NewDecoder(file)
	err = decoder.Decode(&config)
	if err != nil {
		log.Fatalf("error decoding YAML: %v", err)
	}
	return config
}

type Client struct {
	Id                string    `json:"id"`
	Ip                string    `json:"ip"`
	UsedIpCount       int       `json:"usedIpCount"`
	ActiveWindows     string    `json:"activeWindows"`
	LastConnectedTime time.Time `json:"lastConnectedTime"`
	CurTotalTaskCount string    `json:"curTotalTaskCount"`
	CurErrTaskCount   string    `json:"curErrTaskCount"`
}

type IpPool struct {
	startTime       time.Time
	fetchTotalCount int
	config          Config
	clients         map[string]*Client
	fetcher         []*Fetcher
	availableIps    map[string]ProxyItem
	mux             sync.Mutex
}

func (ipPool *IpPool) addIps(items []ProxyItem) {
	ipPool.mux.Lock()
	defer ipPool.mux.Unlock()
	for i, v := range items {
		ipPool.availableIps[v.IP] = items[i]
	}
	ipPool.fetchTotalCount = ipPool.fetchTotalCount + len(items)
}
func (ipPool *IpPool) getFetcherById(fId int) *Fetcher {
	//if fId == ipPool.fetcher[0].Id {
	//	return ipPool.fetcher[0]
	//}
	for i := 0; i < len(ipPool.fetcher); i++ {
		if fId == ipPool.fetcher[i].Id && ipPool.fetcher[i].isAvailable() {
			return ipPool.fetcher[i]
		}
	}
	// 不可能为空
	return nil
}
func (ipPool *IpPool) getOneProxyIp(Id string, activityWindows string, clientIp string, curTotalTaskCount, curErrTaskCount string) ProxyItem {
	ipPool.mux.Lock()
	defer ipPool.mux.Unlock()

	client, has := ipPool.clients[Id]
	if !has {
		client = &Client{Ip: clientIp, Id: Id}
		ipPool.clients[Id] = client
	}
	client.LastConnectedTime = time.Now()
	client.ActiveWindows = activityWindows
	client.CurTotalTaskCount = curTotalTaskCount
	client.CurErrTaskCount = curErrTaskCount
	if len(ipPool.availableIps) > 0 {
		item := ProxyItem{}
		for _, v := range ipPool.availableIps {
			item = v
			break
		}
		if len(item.IP) > 0 {
			delete(ipPool.availableIps, item.IP)
		}
		client.UsedIpCount = client.UsedIpCount + 1
		return item
	}
	return ProxyItem{}
}

type Fetcher struct {
	Id             int       `yaml:"id" json:"id"`
	ProxyUrl       string    `yaml:"url"`
	User           string    `yaml:"user"`
	Password       string    `yaml:"password"`
	LastFetchTime  time.Time `json:"last_fetch_time"`
	FirstStartTime time.Time `json:"first_start_time"`
	FetchCount     int       `json:"fetch_count"`
	Period         int64     `yaml:"period" json:"period"`
	inBroken       bool
	failedCount    int
}

func (f *Fetcher) isAvailable() bool {
	if f.LastFetchTime.IsZero() {
		return true
	}
	if f.failedCount > 10 {
		return false
	}
	if f.Period == 0 {
		return true
	}
	if time.Now().Sub(f.LastFetchTime).Milliseconds()/1e3 <= f.Period {
		return false
	}
	return true
}

func (f *Fetcher) fetchOnce() []ProxyItem {
	ipPool.mux.Lock()
	defer ipPool.mux.Unlock()
	if len(ipPool.availableIps) >= 5 {
		return make([]ProxyItem, 0)
	}

	if f.Id == 2 {
		// 尝试使用ec
		return f.fetchByEC()
	}
	return f.fetchByXM()
}
func (f *Fetcher) fetchByEC() []ProxyItem {
	proxyItems := make([]ProxyItem, 0)
	ctx := context.Background()
	//log.Println("fetchByEC", f.ProxyUrl)
	req, err := http.NewRequestWithContext(ctx, "GET", f.ProxyUrl, nil)
	if err != nil {
		log.Printf("[fetcher] creating request: %v", err)
		return proxyItems
	}
	transport := &http.Transport{
		TLSClientConfig: &tls.Config{
			InsecureSkipVerify: true, // 跳过证书验证
		},
	}
	client := http.Client{Timeout: 5 * time.Second, Transport: transport}
	resp, err := client.Do(req)
	if err != nil {
		log.Printf("[fetcher] request error: %v", err)
		return proxyItems
	}
	defer resp.Body.Close()
	f.LastFetchTime = time.Now()

	if resp.StatusCode < 200 || resp.StatusCode >= 300 {
		log.Printf("[fetcher] non-OK status: %s", resp.Status)
		f.failedCount++
		return proxyItems
	}
	body, err := io.ReadAll(resp.Body)
	if err != nil {
		log.Printf("[fetcher] read body: %v", err)
		return proxyItems
	}
	rs := ECResp{}
	err = json.Unmarshal(body, &rs)
	if err != nil {
		log.Printf("[fetcher] unMarshal : %v", err)
		return proxyItems
	}

	f.FetchCount = f.FetchCount + len(rs.Data)
	if f.FirstStartTime.IsZero() {
		f.FirstStartTime = time.Now()
	}
	if rs.Code != 0 || len(rs.Data) == 0 {
		//f.inBroken = true
		f.failedCount++
		return proxyItems
	}
	for _, datum := range rs.Data {
		ti, _ := time.Parse(time.DateTime, datum.ExpireTime)
		proxyItems = append(proxyItems, ProxyItem{
			IP:         "server.easyhttp.cn",
			Port:       "18888",
			User:       f.User,
			Password:   datum.UniqueKey,
			ExpireTime: ti,
		})
	}
	return proxyItems
}
func (f *Fetcher) fetchByXM() []ProxyItem {
	ctx := context.Background()
	proxyItems := make([]ProxyItem, 0)
	req, err := http.NewRequestWithContext(ctx, "GET", f.ProxyUrl, nil)
	if err != nil {
		log.Printf("[fetcher] creating request: %v", err)
		return proxyItems
	}
	transport := &http.Transport{
		TLSClientConfig: &tls.Config{
			InsecureSkipVerify: true, // 跳过证书验证
		},
	}
	client := http.Client{Timeout: 5 * time.Second, Transport: transport}
	resp, err := client.Do(req)
	if err != nil {
		log.Printf("[fetcher] request error: %v", err)
		return proxyItems
	}
	defer resp.Body.Close()
	if resp.StatusCode < 200 || resp.StatusCode >= 300 {
		log.Printf("[fetcher] non-OK status: %s", resp.Status)
		return proxyItems
	}
	body, err := io.ReadAll(resp.Body)
	if err != nil {

		log.Printf("[fetcher] read body: %v", err)
		return proxyItems
	}
	rs := Resp{}
	err = json.Unmarshal(body, &rs)
	if err != nil {
		log.Printf("[fetcher] unMarshal : %v", err)
		return proxyItems
	}
	f.LastFetchTime = time.Now()
	f.FetchCount = f.FetchCount + len(rs.Obj)
	if f.FirstStartTime.IsZero() {
		f.FirstStartTime = time.Now()
	}
	if rs.Code != "0" || len(rs.Obj) == 0 {
		f.failedCount++
		return proxyItems
	}
	for _, datum := range rs.Obj {
		proxyItems = append(proxyItems, ProxyItem{
			IP:       datum.IP,
			Port:     datum.Port,
			User:     f.User,
			Password: f.Password,
		})
	}
	return proxyItems
}
func getClientIP(r *http.Request) string {
	// 优先从 X-Forwarded-For 获取真实 IP（代理转发时会有）
	// X-Forwarded-For 格式是 "client, proxy1, proxy2, ...", 所以真实 IP 是第一个
	xff := r.Header.Get("X-Forwarded-For")
	if xff != "" {
		// 提取第一个 IP 地址
		ips := strings.Split(xff, ",")
		return strings.TrimSpace(ips[0])
	}

	// 如果没有 X-Forwarded-For，则尝试从 RemoteAddr 获取
	host, _, err := net.SplitHostPort(r.RemoteAddr)
	if err != nil {
		return ""
	}
	return host
}
func getIp(w http.ResponseWriter, r *http.Request) {
	m := make([]ProxyItem, 0)
	w.Header().Set("Content-Type", "application/json")
	Id := r.FormValue("Id")
	if len(Id) == 0 {
		_ = json.NewEncoder(w).Encode(m)
		return
	}
	aw := r.FormValue("aw")
	t := r.FormValue("t")
	ce := r.FormValue("ce")
	// 累计计数
	clientIp := getClientIP(r)
	// 从IP池里面取, 如果有就返回,每次返回一个
	ip := ipPool.getOneProxyIp(Id, aw, clientIp, t, ce)
	if len(ip.IP) > 0 {
		m = append(m, ip)
	}
	_ = json.NewEncoder(w).Encode(m)
}
func domains(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json")
	Id := r.FormValue("Id")
	urls := make([]string, 0)
	if len(Id) == 0 {
		_ = json.NewEncoder(w).Encode(urls)
		return
	}
	type temp struct {
		RandomUrls []string       `json:"randomUrls"`
		ReferUrls  []ReferUrlItem `json:"referUrls"`
	}
	for _, domain := range ipPool.config.Domains {
		if domain.Domain == Id {
			tem := temp{
				RandomUrls: domain.RandomUrls,
				ReferUrls:  domain.ReferUrls,
			}
			_ = json.NewEncoder(w).Encode(tem)
			return
		}
	}
}
func report(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json")
	_ = json.NewEncoder(w).Encode(ipPool.clients)
}
func info(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json")

	type TempFetcher struct {
		Id             int       `json:"id"`
		LastFetchTime  time.Time `json:"last_fetch_time"`
		FirstStartTime time.Time `json:"first_start_time"`
		FetchCount     int       `json:"fetch_count"`
		Period         int64     `json:"period"`
		FailedCount    int       `json:"failed_count"`
	}
	type TempResult struct {
		FetchTotal      int                `json:"fetch_total"`
		ClientInfos     map[string]*Client `json:"client_infos"`
		PoolStartTime   time.Time          `json:"pool_start_time"`
		Fetchers        []TempFetcher      `json:"fetchers"`
		CurAvailableIps int                `json:"cur_available_ips"`
	}
	tempFs := make([]TempFetcher, len(ipPool.fetcher))
	for i, fetcher := range ipPool.fetcher {
		tempF := TempFetcher{
			Id:             fetcher.Id,
			LastFetchTime:  fetcher.LastFetchTime,
			FirstStartTime: fetcher.FirstStartTime,
			FetchCount:     fetcher.FetchCount,
			Period:         fetcher.Period,
			FailedCount:    fetcher.failedCount,
		}
		tempFs[i] = tempF
	}
	t := TempResult{
		FetchTotal:      ipPool.fetchTotalCount,
		ClientInfos:     ipPool.clients,
		Fetchers:        tempFs,
		PoolStartTime:   ipPool.startTime,
		CurAvailableIps: len(ipPool.availableIps),
	}
	_ = json.NewEncoder(w).Encode(t)
}

var ipPool *IpPool

func doFetch(fetcher *Fetcher) {
	for true {
		t := time.Now()
		// 每次10分钟的时候重置
		if t.Minute() == 0 || t.Minute() == 29 {
			fetcher.failedCount = 0
			//continue
		}
		if fetcher.isAvailable() {
			proxyIps := fetcher.fetchOnce()
			ipPool.addIps(proxyIps)
		}
		time.Sleep(time.Millisecond*time.Duration(200) + time.Second)
	}
}
func main() {
	config := getConfig()
	ipPool = &IpPool{availableIps: make(map[string]ProxyItem), mux: sync.Mutex{}, startTime: time.Now(), fetcher: config.Proxies, clients: make(map[string]*Client),
		config: getConfig()}

	for i := 0; i < len(config.Proxies); i++ {
		go doFetch(config.Proxies[i])
	}

	mux := http.NewServeMux()
	// returns JSON array of objects
	mux.HandleFunc("/ip", getIp)
	mux.HandleFunc("/info", info)
	mux.HandleFunc("/domains", domains)
	mux.HandleFunc("/report", report)
	_ = http.ListenAndServe(fmt.Sprintf(":%d", config.Port), mux)
}
